package cn.woochen.comlib.jsbridge

import android.annotation.SuppressLint
import android.content.Context
import android.net.http.SslError
import android.os.Build
import android.os.Looper
import android.os.SystemClock
import android.webkit.*
import cn.woochen.comlib.util.ext.woochenLog
import java.lang.reflect.InvocationTargetException
import java.net.URLEncoder

/**
 *类描述：WebView帮助类(建议放在主module中)
 *@author woochen
 *@time 2021/8/24 14:20
 */
class WebViewHelper(var context:Context,private val webView: WebView) {

    /**
     * 加载url
     */
    fun loadUrl(url: String, headers: MutableMap<String, String> = mutableMapOf()){
        webView.loadUrl(url, headers)
    }

    /**
     * 配置设置
     */
    @SuppressLint("JavascriptInterface")
    fun configSetting(extraConfig: () -> Unit = {}, pageLoadFinish: () -> Unit = {},
        receiveTitle: (String) -> Unit = {}){
        webView.webViewClient = object : WebViewClient() {
            override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
                if (url == null) return true
                if (url.startsWith("http") || url.startsWith("https")) {
                    view?.loadUrl(url)
                    return true
                } else {
                    return true
                }
            }

            override fun onLoadResource(view: WebView?, url: String?) {
                super.onLoadResource(view, url)
//                logcom("资源加载：$url")
            }

            override fun onPageFinished(view: WebView, url: String) {
                super.onPageFinished(view, url)
                webView.settings.blockNetworkImage = false
                //加载本地js文件
                handleOnPageFinished()
                pageLoadFinish.invoke()
            }

            override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?,
                error: SslError?) {
                handler?.proceed()
            }
        }
        val webSettings = webView.settings
        webSettings.javaScriptEnabled = true
        webSettings.javaScriptCanOpenWindowsAutomatically = true
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
        }
        //跨域问题
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            webView.settings.allowUniversalAccessFromFileURLs = true
        } else {
            try {
                val clazz = webView.settings::class.java
                val method =
                    clazz.getMethod("setAllowUniversalAccessFromFileURLs", Boolean::class.javaPrimitiveType)
                method.invoke(webView.settings, true)
            } catch (e: NoSuchMethodException) {
                e.printStackTrace()
            } catch (e: InvocationTargetException) {
                e.printStackTrace()
            } catch (e: IllegalAccessException) {
                e.printStackTrace()
            }

        }
        // 支持缩放(适配到当前屏幕)
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { //少于4.4（不包括4.4）用这个
            webSettings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
        }
        //适应屏幕，大于等于4.4用这个
        webSettings.useWideViewPort = true
        webSettings.domStorageEnabled = true
        webSettings.javaScriptCanOpenWindowsAutomatically = true
        webSettings.loadWithOverviewMode = true
        webSettings.setSupportZoom(true)
        webSettings.blockNetworkImage = true
        webSettings.domStorageEnabled = true
        webSettings.cacheMode = WebSettings.LOAD_DEFAULT

        webView.webChromeClient = object : WebChromeClient() {

            override fun onReceivedTitle(view: WebView, title: String) {
                receiveTitle.invoke(title)
            }

            override fun onJsAlert(view: WebView?, url: String?, message: String?,
                result: JsResult?): Boolean {
                return super.onJsAlert(view, url, message, result)
            }
        }
        extraConfig.invoke()
    }

    /**
     * 保存数据到localStorage(需要在onPageFinished中执行)
     */
    fun saveDataToLocalStorage(webView: WebView, key: String, value: String) {
        val js = "window.localStorage.setItem('${key}','$value');"
        val jsUrl = "javascript:(function({window.localStorage.setItem('${key}','$value')})()"
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            webView.evaluateJavascript(js, null);
        } else {
            webView.loadUrl(jsUrl)
            webView.reload()
        }
    }

    /**
     * 处理页面加载完成逻辑
     */
   private fun handleOnPageFinished() {
        WebBridgeUtil.webViewLoadLocalJs(webView, WebBridgeUtil.JAVA_SCRIPT)
        startupMessage?.forEach {//将本地存储的消息发送给js
            dispatchMessage(it)
        }
        startupMessage = null
    }

    /**
     * 向web发送消息(默认)
     *
     * @param data  消息体
     * @param responseCallback  回调函数
     */
    fun send(handlerName: String?,data: String?, responseCallback: OnBridgeCallback? = null) {
        doSend(handlerName, data, responseCallback)
    }

    /**
     * native调用web方法后，异步回调数据给web
     */
    fun response(data: String?,responseId:String?){
        val m = Message()
        m.responseId = responseId
        m.responseData = data
        queueMessage(m)
        woochenLog("response:${m}")
    }

    private var uniqueId: Long = 0 //回调id

    val responseCallbacks = mutableMapOf<String, OnBridgeCallback>()//回调函数缓存


    private var startupMessage:MutableList<Message>? = mutableListOf()//onFinish之前发送的消息缓存

    /**
     * 构造消息对象
     * @param handlerName      web中注册的handlerName，默认为null
     * @param data             消息体
     * @param responseCallback 回调函数
     */
    private fun doSend(handlerName: String?, data: String?, responseCallback: OnBridgeCallback?) {
        val m = Message()
        m.data = data
        m.handlerName = handlerName
        if (responseCallback != null) {
            val callbackStr = String.format(WebBridgeUtil.CALLBACK_ID_FORMAT, "${++uniqueId}_${SystemClock.currentThreadTimeMillis()}")//JAVA_CB_1_10248545657412
            m.callbackId = callbackStr
            responseCallbacks[callbackStr] = responseCallback
        }
        queueMessage(m)
    }

    /**
     * startupMessage != null 添加到消息集合否则分发消息
     */
    private fun queueMessage(m: Message) {
        //如果onPageFinish没有被调用(即js文件还没有被加载)，将消息存在本地
        if (startupMessage != null){
            startupMessage?.add(m)
        }else{//js文件已经加载，直接发送消息给js
            dispatchMessage(m)
        }
    }

    /**
     * 分发message 必须在主线程才分发成功
     *
     * @param m Message
     */
    private fun dispatchMessage(m: Message) {
        var messageJson = m.toJson()
        //为json字符串转义特殊字符
        messageJson = messageJson?.apply {
            replace("(\\\\)([^utrn])".toRegex(), "\\\\\\\\$1$2")
            replace("(?<=[^\\\\])(\")".toRegex(), "\\\\\"")
            replace("(?<=[^\\\\])(\')".toRegex(), "\\\\\'")
            replace("%7B".toRegex(), URLEncoder.encode("%7B", "utf-8"))
            replace("%7D".toRegex(), URLEncoder.encode("%7D", "utf-8"))
            replace("%22".toRegex(), URLEncoder.encode("%22", "utf-8"))
        }
        //WebViewJavascriptBridge._handleMessageFromNative(messageJSON)
        val javascriptCommand = String.format(WebBridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson)
        // 必须要找主线程才会将数据传递出去 --- 划重点
        if (Thread.currentThread() === Looper.getMainLooper().thread) {
            webView.loadUrl(javascriptCommand)
        }
    }

}